Power and electricity are pivotal elements that allow the modern world to function. As technology progresses, humanity has an ever increasing dependency on electricity. Current production methods result in emissions that harm the environment. The EPA is interested in identifying factors that help to reduce CO2, SO2, and NOX emissions. Using a dataset containing emission characteristics from electric power plants in the US in 2020, I will analyze the following questions for combustion power plants:
library(tidyverse)
library(tigris)
library(censusapi)
library(sf)
library(mapview)
library(plotly)
library(leaflet)
library(plotly)
library(data.table)
mapviewOptions(
basemaps = "OpenStreetMap"
)
options(
tigris_class = "sf",
tigris_use_cache = T # This stores tigris loads somewhere on your machine for much faster personal loading.
)
setwd("~/CEE 322/A1")
load("plants.Rdata")
#The goal is tounderstand what problems there are in emissions are what areas the EPA should focus on toaddress these.
For this analysis, I will be using a dataset that contains emissions characteristics of electric power plants in the United States for 2010. The dataset contains 5393 observations which represent individual power plants in the United States. There are 38 variables with power plant characteristics, which include:
The dataset is complete for all relevant values.
In preparation for the analysis, I created 5 new variables which include: * Capacity factor: ratio of a power plant’s annual actual output to its potential output at full nameplate capacity. * Nominal Heat Rate: Efficiency of how the primary fuel is converted to electric energy. Calculated for combustion and partial combustion plants only. * CO2, NOX, and SO2 Emission Rate: Quantifies the amount of gas pollutants that are generated per unit of electricity generation (for combustion plants only).
I utilize these value for comparisons in the analysis section.
df_modified <-
df %>%
mutate(
CapFac = NetGen / (Capacity * 8760),
HeatRate = case_when(
Combust == 1 ~ 1000 * (HeatInput / NetGen),
Combust == 0.5 ~ 1000 * (HeatInput / CombGen),
Combust == 0 ~ NaN
),
SO2Rate = 2000 * (SO2 / NetGen),
CO2Rate = 2000 * (CO2 / NetGen),
NOXRate = 2000 * (NOX / NetGen)
)
total_generation <- sum(df_modified$NetGen)
some_combustion <-
df_modified %>%
filter(Combust %in% c(1,0.5))
percent_combustion <- sum(some_combustion$NetGen) / total_generation
partial_combustion <-
some_combustion %>%
filter(Combust == 0.5)
#fraction of plants that are partial combustion out of combined combustion/partial combustion
partial_combustion_frac <- nrow(partial_combustion) / nrow(some_combustion)
#accounts for only 2%
partial_combustion_gen_proportion <- sum(partial_combustion$NetGen) / total_generation
partial_combustion <-
some_combustion %>%
filter(Combust == 0.5)
#fraction of plants that are partial combustion out of combined combustion/partial combustion
partial_combustion_frac <- nrow(partial_combustion) / nrow(some_combustion)
#accounts for only 2%
partial_combustion_gen_proportion <- sum(partial_combustion$NetGen) / total_generation
For this analysis, we only want to assess the impact of combustion power plants on pollutant emissions. In order to justify this decision, it is important to quantify the impact of combustion plants on electric power generation in the United States. Out of all power plants (of types combustion, partial combustion, and no combustion), 73% of electric power is generated by either partial-combustion or combustion plants. Since we are only interested in combustion plants for this analysis, I removed 2089 observations that correspond with no combustion.
Within the partial/full combustion category, only 2% of the plants are partial-combustion. Additionally, partial-combustion plants only account for 2% of the US electric power generation. Because the effect of partial-combustion plants on electricity production is so minimal, I removed these plants from consideration (71 observations in total). At this point, I am only analyzing full combustion plants.
full_combustion <-
some_combustion %>%
filter(Combust == 1)
#types of primary fuel type for full combustion plants
primary_types <- unique(full_combustion$FuelCat)
primary <- function(primary_fuel){
full_combustion %>%
filter(FuelCat == primary_fuel) %>%
dplyr::select(FuelCat, CoalGen,OilGen,GasGen, NuclearGen, HydroGen, BiomassGen,WindGen,SolarGen,GeoGen,OtherFossilGen,OtherGen) %>%
mutate(
FracCoal = CoalGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100,
FracOil = OilGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100,
FracGas = GasGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100,
FracOtherFossil = OtherFossilGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100,
FracBiomass = BiomassGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100,
FracOther = OtherGen / (CoalGen + OilGen + GasGen + NuclearGen + HydroGen + BiomassGen + WindGen + SolarGen + GeoGen + OtherFossilGen + OtherGen)*100
) %>%
group_by(FuelCat) %>%
summarise(FracCoal = mean(FracCoal),FracOil = mean(FracOil),FracGas = mean(FracGas), FracOtherFossil = mean(FracOtherFossil),FracBiomass = mean(FracBiomass),FracOther = mean(FracOther))
}
#WSTHTOTPUR classified under OtherGen category...some have fuel type of NG
#analysis for why we only need to consider primary energy
primary_coal <- primary("COAL") %>% select(FuelCat,FracCoal) %>% rename(PrimaryFrac = "FracCoal")
primary_gas <- primary("GAS") %>% select(FuelCat,FracGas) %>% rename(PrimaryFrac = "FracGas")
primary_oil <- primary("OIL")%>% select(FuelCat,FracOil) %>% rename(PrimaryFrac = "FracOil")
primary_other_fossil <- primary("OTHRFOSL")%>% select(FuelCat,FracOtherFossil) %>% rename(PrimaryFrac = "FracOtherFossil")
primary_biomass <- primary("BIOMASS")%>% select(FuelCat,FracBiomass) %>% rename(PrimaryFrac = "FracBiomass")
primary_other <- primary("WSTHTOTPUR")%>% select(FuelCat,FracOther) %>% rename(PrimaryFrac = "FracOther")
primary_analysis <-
primary_coal %>%
rbind(primary_gas) %>%
rbind(primary_oil) %>%
rbind(primary_other_fossil) %>%
rbind(primary_biomass) %>%
rbind(primary_other) %>%
arrange(desc(PrimaryFrac))
primary_plot <- plot_ly(primary_analysis %>% arrange(), x = ~FuelCat, y = ~PrimaryFrac, type = 'bar', name = 'barchart') %>%
layout(
title = "Primary Fuel Generation Out of Total Generation",
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Primary Fuel Source",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE,
range = c(-1,6)
),
yaxis = list(
title = "Primary Fuel Generation Fraction (%)",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE,
fixedrange = T,
range = c(80,100)
)
)
In the dataset, each power plant has a primary fuel source, but some also have electric power generation associated with multiple other fuels. Overall, the generation from these non-primary fuel sources make up only a small fraction of the total generation. The histogram in Fig. 1 illustrates the average fraction of generation for plants with different primary fuel source types compared to the total generation of those plants. From this, we conclude that the majority of electric power generation is attributed to the primary fuel source for combustion plants. The following outlines the average percentage of total plant power generation attributed to each plant primary fuel type:
Between 87% and 97.3% of electric power generation for all combustion plants in the dataset is attributed to the primary fuel source. Since this represents the majority of generation, I will only consider the primary fuel source for comparison for further analysis.
primary_plot
Figure 1: Percentage of Primary Fuel Generation Out of Total Generation
Now, I will examine emissions by fuel type. First, I will compare average CO2 emissions for combustion plants to the plant’s fuel type. From Fig. 2, we see that the following fuel types have the highest CO2 emissions: * LIG (lignite coal): short tons * SUB (subbituminous coal): short tons * BIT (bituminous coal): short tons
These plants all have some form of coal as the primary fuel type, so we can conclude that coal is a major contributor to CO2 emissions.
CO2 <-
full_combustion %>%
group_by(Fuel) %>%
summarise(CO2 = mean(CO2)) %>%
arrange(CO2) %>%
filter(CO2 != 0)
c02_plot <- plot_ly(CO2, x = ~Fuel, y = ~CO2, type = 'bar', name = 'barchart') %>%
layout(
title = "Average CO2 Emissions By Fuel Type",
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Fuel Type",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
),
yaxis = list(
title = "Average CO2 Emissions (short tons)",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
)
)
c02_plot
Figure 2: Average Annual CO2 Emissions by Each Primary Fuel Type
When examining SO2 and NOx by primary fuel type, we see that LIG, SUB, and BIT also have the highest emissions. The Figure 3 and Figure 4 show SO2 and NOX emissions by fuel type, respectively.
SO2 <-
full_combustion %>%
group_by(Fuel) %>%
summarise(SO2 = mean(SO2)) %>%
arrange(SO2) %>%
filter(SO2 != 0)
sO2_plot <- plot_ly(SO2, x = ~Fuel, y = ~SO2, type = 'bar', name = 'barchart') %>%
layout(
title = "Average SO2 Emissions By Fuel Type",
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Fuel Type",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
),
yaxis = list(
title = "Average SO2 Emissions (short tons)",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
)
)
sO2_plot
Figure 3: Average Annual SO2 Emissions by Each Primary Fuel Type
NOX <-
full_combustion %>%
group_by(Fuel) %>%
summarise(NOX = mean(NOX)) %>%
arrange(NOX) %>%
filter(NOX != 0)
NOX_plot <- plot_ly(NOX, x = ~Fuel, y = ~NOX, type = 'bar', name = 'barchart') %>%
layout(
title = "Average NOX Emissions By Fuel Type",
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Fuel Type",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
),
yaxis = list(
title = "Average NOX Emissions (short tons)",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
)
)
NOX_plot
Figure 4: Average Annual NOX Emissions by Each Primary Fuel Type
The amount of emissions from SO2 and NOX (in short tons) is far less than that of CO2 emissions. When examining all emissions on a stacked bar chart (Figure 5), the effect of SO2 and NOX is barely visible in comparison to CO2. At this point, I conclude that CO2 is the main pollutant to target for reduction, and the best way to do so is to eliminate the various types of coal as primary fuel source types.
emissions_fuel <-
CO2 %>%
left_join(
SO2,
by = "Fuel"
) %>%
left_join(
NOX,
by = "Fuel"
) %>%
mutate(
total_emissions = CO2+SO2+NOX
) %>%
arrange(total_emissions)
emission <-
full_combustion %>%
group_by(Fuel) %>%
summarise(CO2 = mean(CO2), SO2 = mean(SO2), NOX= mean(NOX)) %>%
filter(CO2 != 0) %>%
melt()
emission_plot <- plot_ly(emission, x = ~Fuel, y = ~value, type = 'bar', name = ~variable, color = ~variable) %>%
layout(
title = "Emissions By Fuel Type",
barmode = 'stack',
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Fuel Type",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
),
yaxis = list(
title = "Emissions (Short Tons)",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
)
)
emission_plot
Figure 5: CO2, NOX, and SO2 emission by fuel type comparison (color-coded by pollutant)
When examining emissions from a different angle, that is the amount of gas pollutant generated per unit of electricity (emission rate), we see that coal is no longer the highest contributor (Figure 6). Instead, the fuel OTS (other solid) has the highest pollutant emission rate at 24,303 pounds of pollutant per MWh. In this picture, LIG, SUB, and BIT (which had the highest CO2 emission rates in the previous graph) are in the average range of emission rates. This implies that coal is fairly efficient in the amount of electricity it generates compared to the amount of CO2 emissions it creates. The fuel category OTS, on the other hand, is very inefficient and creates a significant amount of CO2 per MWh.
emissionRate <-
full_combustion %>%
group_by(Fuel) %>%
summarise(CO2Rate = mean(CO2Rate), SO2Rate = mean(SO2Rate), NOXRate = mean(NOXRate)) %>%
filter(CO2Rate != 0) %>%
melt()
emissionRate_plot <- plot_ly(emissionRate, x = ~Fuel, y = ~value, type = 'bar', name = ~variable, color = ~variable) %>%
layout(
title = "Average Annual Output Emission Rate by Fuel Type",
barmode = 'stack',
margin = list(t = 25),
paper_bgcolor='rgb(255,255,255)',
plot_bgcolor='rgb(229,229,229)',
xaxis = list(
title = "Fuel Type",
gridcolor = 'rgb(255,255,255)',
showgrid = FALSE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
),
yaxis = list(
title = "Average Annual Output Emission Rate",
gridcolor = 'rgb(255,255,255)',
showgrid = TRUE,
showline = FALSE,
showticklabels = TRUE,
tickcolor = 'rgb(127,127,127)',
ticks = 'outside',
zeroline = FALSE
)
)
emissionRate_plot
#Co2 has highest emission rate, OTS fuel type the most
Figure 6: Pollutant emission rate by fuel type. OTS has the highest values
Now, I will compare CO2 emissions by the region a power plant is located to explore if there is any correlation between location and emissions.
The Figure 7 displays CO2 emissions by latitude. There are high CO2 emissions in the latitude range of 25 to 50. This range captures the majority of the continental US, and the outliers with lower CO2 emissions are likely from Hawaii or Alaska. The highest CO2 emissions occur around latitude 35 (which is captured approximately by the horizontal area between Las Vegas, NV and Dallas, TX). From this plot, there appears to be regional trends for CO2 emissions.
plot(full_combustion$Lat,full_combustion$CO2,main = "CO2 Emissions by Latitude", xlab = "Latitude", ylab = "CO2 Emissions (short tons)")
Figure 7: Co2 emissions by latitude
For longitude (Figure 8), there is also regional clustering for CO2 emissions in the range of -120 to -90 (again, the continental US). The highest values occur at approximately -80 longitude. Combining this information from the latitude comparison above, I pinpoint the area with the highest CO2 emission to be near Georgia (indeed, the plant with the highest CO2 emissions is the Scherer plant in Monroe county, Georgia).
plot(full_combustion$Lon,full_combustion$CO2, main = "CO2 Emissions by Longitude", xlab = "Longitude", ylab = "CO2 Emissions (short tons)")
Figure 8: CO2 emissions by longitude
Because there seems to be a regional effect on CO2 emissions, I will now visually examine each state’s contribution to CO2, SO2, and NOX emissions. The map in Figure 9 shows each state color-coded by its total annual emission amounts. You can change the emission displayed by selecting a different radio button on the map. The last radio button shows the location of all power plants in the dataset.
The following observations relate to Figure 9:
CO2 Emissions One state stands out prominently for CO2 emissions: Texas. It is much higher than any other state at ~26 million short tons of annual CO2 emissions. The second place state for CO2 emissions is Pennsylvania at ~14 million short tons, which is nearly half of the Texas CO2 emissions.
SO2 Emissions Ohio has the highest SO2 emissions (~600k short tons annually), followed by Texas with ~460k SO2 emissions.
NOX Emissions Once again, Texas lead in annual NOX emissions at ~160k short tons annually. Pennsylvania follows close behind at ~140k short tons of NOX annual emission.
state_analysis <-
full_combustion %>%
group_by(State) %>%
summarise(CO2 = sum(CO2),SO2 = sum(SO2),NOX = sum(NOX))
state_geom <-
states(cb = TRUE, progress_bar = FALSE) %>%
st_transform(4326)
state_analysis_map <-
state_analysis %>%
left_join(
state_geom,
by = c("State" = "STUSPS")
) %>%
st_as_sf() %>%
st_transform(4326)
red_pal <- colorNumeric(
palette = colorRamp(c("#fae1e1", "#fc0303"), interpolate="spline"),
domain =
state_analysis_map %>%
pull(CO2) %>%
unique()
)
blue_pal <- colorNumeric(
palette = colorRamp(c("#dee0ff", "#000dff"), interpolate="spline"),
domain =
state_analysis_map %>%
pull(SO2) %>%
unique()
)
green_pal <- colorNumeric(
palette = colorRamp(c("#c7f2cc", "#00730f"), interpolate="spline"),
domain =
state_analysis_map %>%
pull(NOX) %>%
unique()
)
state_plot <-
leaflet() %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addPolygons(
data = state_analysis_map,
fillColor = ~red_pal(CO2),
color = "white",
weight = 0.5,
opacity = 0.5,
fillOpacity = 0.7,
group = "CO2 Emissions",
label = ~paste0(CO2," short tons of CO2 in ",State),
highlightOptions =
highlightOptions(
weight = 2.25,
opacity = 1
)
) %>%
addPolygons(
data = state_analysis_map,
fillColor = ~blue_pal(SO2),
color = "white",
weight = 0.5,
opacity = 0.5,
fillOpacity = 0.7,
group = "SO2 Emissions",
label = ~paste0(SO2," short tons of SO2 in ",State),
highlightOptions =
highlightOptions(
weight = 2.25,
opacity = 1
)
) %>%
addPolygons(
data = state_analysis_map,
fillColor = ~green_pal(NOX),
color = "white",
weight = 0.5,
opacity = 0.5,
fillOpacity = 0.7,
group = "NOX Emissions",
label = ~paste0(NOX," short tons of NOX in ",State),
highlightOptions =
highlightOptions(
weight = 2.25,
opacity = 1
)
) %>%
addCircles(
data = full_combustion %>%
st_as_sf(coords = c("Lon","Lat"), crs = 4326),
color = "black",
weight = 0.5,
opacity = 0.5,
fillOpacity = 0.7,
label = ~paste0("Power Plant: ",Name),
group = "Power Plant Locations",
highlightOptions =
highlightOptions(
weight = 2.25,
opacity = 1
)
) %>%
addLayersControl(
baseGroups = c("CO2 Emissions", "SO2 Emissions", "NOX Emissions","Power Plant Locations"),
options = layersControlOptions(collapsed = FALSE)
) %>%
setView(-107.913237,41.991641,3)
state_plot